using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Storage.Streams;
using Buffer = Windows.Storage.Streams.Buffer;

namespace Windows.Storage
{
    /// <summary>
    /// Provides helper methods for reading and writing files that are represented by objects of type IStorageFile.
    /// </summary>
    public static class FileIO
    {
        /// <summary>
        /// Reads the contents of the specified file and returns text.
        /// </summary>
        /// <param name="file">The file to read.</param>
        /// <returns>When this method completes successfully, it returns the contents of the file as a text string.</returns>
        public static IAsyncOperation<string> ReadTextAsync(IStorageFile file)
        {
            return ReadTextAsyncInternal(file).AsAsyncOperation();
        }

        /// <summary>
        /// Reads the contents of the specified file using the specified character encoding and returns text.
        /// </summary>
        /// <param name="file">The file to read.</param>
        /// <param name="encoding">The character encoding to use.</param>
        /// <returns>When this method completes successfully, it returns the contents of the file as a text string.</returns>
        public static IAsyncOperation<string> ReadTextAsync(IStorageFile file, UnicodeEncoding encoding)
        {
            return ReadTextAsyncInternal(file, encoding).AsAsyncOperation();
        }

        /// <summary>
        /// Writes text to the specified file.
        /// </summary>
        /// <param name="file">The file that the text is written to.</param>
        /// <param name="contents">The text to write.</param>
        /// <returns>No object or value is returned when this method completes.</returns>
        public static IAsyncAction WriteTextAsync(IStorageFile file, string contents)
        {
            return WriteTextAsyncInternal(file, contents).AsAsyncAction();
        }

        /// <summary>
        /// Writes text to the specified file using the specified character encoding.
        /// </summary>
        /// <param name="file">The file that the text is written to.</param>
        /// <param name="contents">The text to write.</param>
        /// <param name="encoding">The character encoding of the file.</param>
        /// <returns>No object or value is returned when this method completes.</returns>
        public static IAsyncAction WriteTextAsync(IStorageFile file, string contents, UnicodeEncoding encoding)
        {
            return WriteTextAsyncInternal(file, contents, encoding).AsAsyncAction();
        }

        /// <summary>
        /// Appends text to the specified file.
        /// </summary>
        /// <param name="file">The file that the text is appended to.</param>
        /// <param name="contents">The text to append.</param>
        /// <returns>No object or value is returned when this method completes.</returns>
        public static IAsyncAction AppendTextAsync(IStorageFile file, string contents)
        {
            return AppendTextAsyncInternal(file, contents).AsAsyncAction();
        }

        /// <summary>
        /// Appends text to the specified file using the specified character encoding.
        /// </summary>
        /// <param name="file">The file that the text is appended to.</param>
        /// <param name="contents">The text to append.</param>
        /// <param name="encoding">The character encoding of the file.</param>
        /// <returns>No object or value is returned when this method completes.</returns>
        public static IAsyncAction AppendTextAsync(IStorageFile file, string contents, UnicodeEncoding encoding)
        {
            return AppendTextAsyncInternal(file, contents, encoding).AsAsyncAction();
        }

        /// <summary>
        /// Reads the contents of the specified file and returns lines of text.
        /// </summary>
        /// <param name="file">The file to read.</param>
        /// <returns>When this method completes successfully, it returns the contents of the file as a list (type IVector) of lines of text. Each line of text in the list is represented by a String object.</returns>
        public static IAsyncOperation<IList<string>> ReadLinesAsync(IStorageFile file)
        {
            return ReadLinesAsyncInternal(file).AsAsyncOperation();
        }

        /// <summary>
        /// Reads the contents of the specified file using the specified character encoding and returns lines of text.
        /// </summary>
        /// <param name="file">The file to read.</param>
        /// <param name="encoding">The character encoding to use.</param>
        /// <returns>When this method completes successfully, it returns the contents of the file as a list (type IVector) of lines of text. Each line of text in the list is represented by a String object.</returns>
        public static IAsyncOperation<IList<string>> ReadLinesAsync(IStorageFile file, UnicodeEncoding encoding)
        {
            return ReadLinesAsyncInternal(file, encoding).AsAsyncOperation();
        }

        /// <summary>
        /// Writes lines of text to the specified file.
        /// </summary>
        /// <param name="file">The file that the lines are written to.</param>
        /// <param name="lines">The list of text strings to write as lines.</param>
        /// <returns>No object or value is returned when this method completes.</returns>
        public static IAsyncAction WriteLinesAsync(IStorageFile file, IEnumerable<string> lines)
        {
            return WriteLinesAsyncInternal(file, lines).AsAsyncAction();
        }

        /// <summary>
        /// Writes lines of text to the specified file using the specified character encoding.
        /// </summary>
        /// <param name="file">The file that the lines are written to.</param>
        /// <param name="lines">The list of text strings to write as lines.</param>
        /// <param name="encoding">The character encoding of the file.</param>
        /// <returns>No object or value is returned when this method completes.</returns>
        public static IAsyncAction WriteLinesAsync(IStorageFile file, IEnumerable<string> lines, UnicodeEncoding encoding)
        {
            return WriteLinesAsyncInternal(file, lines, encoding).AsAsyncAction();
        }

        /// <summary>
        /// Appends lines of text to the specified file.
        /// </summary>
        /// <param name="file">The file that the lines are appended to.</param>
        /// <param name="lines">The list of text strings to append as lines.</param>
        /// <returns>No object or value is returned when this method completes.</returns>
        public static IAsyncAction AppendLinesAsync(IStorageFile file, IEnumerable<string> lines)
        {
            return AppendLinesAsyncInternal(file, lines).AsAsyncAction();
        }

        /// <summary>
        /// Appends lines of text to the specified file using the specified character encoding.
        /// </summary>
        /// <param name="file">The file that the lines are appended to.</param>
        /// <param name="lines">The list of text strings to append as lines.</param>
        /// <param name="encoding">The character encoding of the file.</param>
        /// <returns>No object or value is returned when this method completes.</returns>
        public static IAsyncAction AppendLinesAsync(IStorageFile file, IEnumerable<string> lines, UnicodeEncoding encoding)
        {
            return AppendLinesAsyncInternal(file, lines, encoding).AsAsyncAction();
        }

        /// <summary>
        /// Reads the contents of the specified file and returns a buffer.
        /// </summary>
        /// <param name="file">The file to read.</param>
        /// <returns>When this method completes, it returns an object (type IBuffer) that represents the contents of the file.</returns>
        public static IAsyncOperation<IBuffer> ReadBufferAsync(IStorageFile file)
        {
            return ReadBufferAsyncInternal(file).AsAsyncOperation();
        }

        /// <summary>
        /// Writes data from a buffer to the specified file.
        /// </summary>
        /// <param name="file">The file that the buffer of data is written to.</param>
        /// <param name="buffer">The buffer that contains the data to write.</param>
        /// <returns>No object or value is returned when this method completes.</returns>
        public static IAsyncAction WriteBufferAsync(IStorageFile file, IBuffer buffer)
        {
            return WriteBufferAsyncInternal(file, buffer).AsAsyncAction();
        }

        /// <summary>
        /// Writes an array of bytes of data to the specified file.
        /// </summary>
        /// <param name="file">The file that the byte is written to.</param>
        /// <param name="buffer">The array of bytes to write.</param>
        /// <returns>No object or value is returned when this method completes.</returns>
        public static IAsyncAction WriteBytesAsync(IStorageFile file, byte[] buffer)
        {
            return WriteBytesAsyncInternal(file, buffer).AsAsyncAction();
        }

        #region Internal implementation

        internal static async Task<string> ReadTextAsyncInternal(IStorageFile file)
        {
            using (Stream stream = await file.OpenStreamForReadAsync())
            {
                using (StreamReader streamReader = new StreamReader(stream))
                {
                    return streamReader.ReadToEnd();
                }
            }
        }

        internal static async Task<string> ReadTextAsyncInternal(IStorageFile file, UnicodeEncoding encoding)
        {
            using (Stream stream = await file.OpenStreamForReadAsync())
            {
                using (StreamReader streamReader = new StreamReader(stream, encoding.ToEncoding()))
                {
                    return streamReader.ReadToEnd();
                }
            }
        }

        internal static async Task WriteTextAsyncInternal(IStorageFile file, string contents)
        {
            using (Stream current = await file.OpenStreamForWriteAsync())
            {
                using (StreamWriter sw = new StreamWriter(current))
                {
                    await sw.WriteAsync(contents);
                }
            }
        }

        internal static async Task WriteTextAsyncInternal(IStorageFile file, string contents, UnicodeEncoding encoding)
        {
            using (Stream current = await file.OpenStreamForWriteAsync())
            {
                using (StreamWriter sw = new StreamWriter(current, encoding.ToEncoding()))
                {
                    await sw.WriteAsync(contents);
                }
            }
        }

        internal static async Task AppendTextAsyncInternal(IStorageFile file, string contents)
        {
            using (StreamWriter sw = new StreamWriter(file.Path, true))
            {
                await sw.WriteAsync(contents);
            }
        }

        internal static async Task AppendTextAsyncInternal(IStorageFile file, string contents, UnicodeEncoding encoding)
        {
            using (StreamWriter sw = new StreamWriter(file.Path, true, encoding.ToEncoding()))
            {
                await sw.WriteAsync(contents);
            }
        }

        internal static async Task<IList<string>> ReadLinesAsyncInternal(IStorageFile file)
        {
            using (Stream stream = await file.OpenStreamForReadAsync())
            {
                using (StreamReader streamReader = new StreamReader(stream))
                {
                    // TODO use ReadLineAsync?
                    IList<string> list = new List<string>();
                    string line;
                    while ((line = streamReader.ReadLine()) != null)
                    {
                        list.Add(line);
                    }
                    return list;
                }
            }
        }

        internal static async Task<IList<string>> ReadLinesAsyncInternal(IStorageFile file, UnicodeEncoding encoding)
        {
            using (Stream stream = await file.OpenStreamForReadAsync())
            {
                using (StreamReader streamReader = new StreamReader(stream, encoding.ToEncoding()))
                {
                    // TODO use ReadLineAsync?
                    IList<string> list = new List<string>();
                    string line;
                    while ((line = streamReader.ReadLine()) != null)
                    {
                        list.Add(line);
                    }
                    return list;
                }
            }
        }

        internal static async Task WriteLinesAsyncInternal(IStorageFile file, IEnumerable<string> lines)
        {
            using (Stream current = await file.OpenStreamForWriteAsync())
            {
                using (StreamWriter sw = new StreamWriter(current))
                {
                    foreach (string line in lines)
                    {
                        // TODO use WriteLineAsync?
                        sw.WriteLine(line);
                    }
                }
            }
        }

        internal static async Task WriteLinesAsyncInternal(IStorageFile file, IEnumerable<string> lines, UnicodeEncoding encoding)
        {
            using (Stream current = await file.OpenStreamForWriteAsync())
            {
                using (StreamWriter sw = new StreamWriter(current, encoding.ToEncoding()))
                {
                    foreach (string line in lines)
                    {
                        // TODO use WriteLineAsync?
                        sw.WriteLine(line);
                    }
                }
            }
        }

        internal static Task AppendLinesAsyncInternal(IStorageFile file, IEnumerable<string> lines)
        {
            using (StreamWriter sw = new StreamWriter(file.Path, true))
            {
                foreach (string line in lines)
                {
                    // TODO use WriteLineAsync?
                    sw.WriteLine(line);
                }
            }
            return Task.FromResult(0);
        }

        internal static Task AppendLinesAsyncInternal(IStorageFile file, IEnumerable<string> lines, UnicodeEncoding encoding)
        {
            using (StreamWriter sw = new StreamWriter(file.Path, true, encoding.ToEncoding()))
            {
                foreach (string line in lines)
                {
                    // TODO use WriteLineAsync?
                    sw.WriteLine(line);
                }
            }
            return Task.FromResult(0);
        }

        internal static async Task<IBuffer> ReadBufferAsyncInternal(IStorageFile file)
        {
            using (IRandomAccessStream randomAccessStream = await file.OpenReadAsync())
            {
                uint size = (uint)randomAccessStream.Size;
                IBuffer buffer = new Buffer(size);
                await randomAccessStream.ReadAsync(buffer, size, InputStreamOptions.None);
                return buffer;
            }
        }

        internal static async Task WriteBufferAsyncInternal(IStorageFile file, IBuffer buffer)
        {
            using (IRandomAccessStream randomAccessStream = await file.OpenAsync(FileAccessMode.ReadWrite))
            {
                await randomAccessStream.WriteAsync(buffer);
            }
        }

        internal static async Task WriteBytesAsyncInternal(IStorageFile file, byte[] buffer)
        {
            using (Stream stream = await file.OpenStreamForWriteAsync())
            {
                await stream.WriteAsync(buffer, 0, buffer.Length);
            }
        }

        #endregion
    }
}